package com.common.model.pojo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.common.model.annotation.RealField;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 查询参数字段
 *  更复杂的查询自行想办法
 *  请求样式
 *  <b>
 *      {
 *   "current": 1,
 *   "orders": [
 *     {
 *       "asc": true,
 *       "orderField": "sort"
 *     },
 *     {
 *       "asc": false,
 *       "orderField": "updateTime"
 *     }
 *   ],
 *   "keywords": [
 *     {
 *       "keywordField": "name",
 *       "keyword": "数据"
 *     }
 *   ],
 *   "params": {
 *     "pid": "0",
 *     "platform": "FLINK"
 *   },
 *   "size": 10
 * }
 *  </b>
 *  这个对象不能轻易动
 * @author ccf
 */
@Data
@Accessors(chain = true)
@ApiModel(description = "查询条件")
@NoArgsConstructor
@AllArgsConstructor
public class Query {

    /**
     * 当前页
     */
    @ApiModelProperty(value = "当前页")
    private Integer current;

    /**
     * 每页的数量
     */
    @ApiModelProperty(value = "每页的数量")
    private Integer size;

    /**
     * 排序规则
     */
    @ApiModelProperty(value = "排序")
    private List<Order> orders;

    /**
     * 查询关键字
     */
    @ApiModelProperty(value = "查询关键字提供给全文检索")
    private String keyword;

    /**
     * 查询关键字
     */
    @ApiModelProperty(value = "查询关键字")
    private List<Keyword> keywords = new ArrayList<>();

    /**
     * 查询关键字
     */
    @ApiModelProperty(value = "精确查找")
    private Map<String, String> params = new HashMap<>();

    /**
     * 前端会传递空对象过来
     * @return
     */
    public List<Order> filterNullOrders() {
        if(orders != null){
            return orders.stream().filter(e-> StrUtil.isNotBlank(e.getOrderField())).collect(Collectors.toList());
        }
        return null;
    }

    public List<Keyword> filterNullKeywords() {
        if(keywords != null){
            return keywords.stream().filter(e-> StrUtil.isNotBlank(e.getKeyword())).collect(Collectors.toList());
        }
        return null;
    }

    public Keyword getKeywordByFiledName(String name){
        List<Keyword> keywordList = filterNullKeywords();
        if (keywordList == null){
            return null;
        }
        return keywordList.stream().filter(e-> StrUtil.equals(e.getKeywordField(), name)).findFirst().orElse(null);
    }

    public Order getOrderByFiledName(String name){
        List<Order> orders = filterNullOrders();
        if (orders == null){
            return null;
        }
        return orders.stream().filter(e-> StrUtil.equals(e.getOrderField(), name)).findFirst().orElse(null);
    }

    public void addParams(String key, String value){
        if (params == null){
            params = MapBuilder.<String, String>create().put(key, value).build();
        } else {
            params.put(key, value);
        }
    }

    public void convert(Class<?> clazz){
        Field[] fields = ReflectUtil.getFields(clazz);
        List<Keyword> keywordList = filterNullKeywords();
        keywordList = keywordList == null? Lists.newArrayList():keywordList;
        Set<String> keywordSet = keywordList.stream().map(e -> e.getKeywordField()).collect(Collectors.toSet());
        List<Order> orders = filterNullOrders();
        orders = orders == null? Lists.newArrayList():orders;
        Set<String> orderSet = orders.stream().map(e -> e.getOrderField()).collect(Collectors.toSet());
        for (Field field: fields) {
            RealField annotation = field.getAnnotation(RealField.class);
            if (annotation != null && StrUtil.isNotBlank(annotation.value())){
                String fieldName = field.getName();
                if (this.getParams().containsKey(fieldName)){
                    String value = this.getParams().get(fieldName);
                    this.getParams().put(annotation.value(), value);
                    this.getParams().remove(fieldName);
                }
                if (keywordSet.contains(fieldName)){
                    Keyword keyword = getKeywordByFiledName(fieldName);
                    keyword.setKeywordField(annotation.value());
                }
                if (orderSet.contains(fieldName)){
                    Order order = getOrderByFiledName(fieldName);
                    order.setOrderField(annotation.value());
                }
            }
        }

    }

    /**
     * 为查询条件添加delFlag标记
     * @return
     */
    public void addDelFlag(){
        Map<String, String> params = this.getParams();
        if (MapUtil.isEmpty(params)){
            this.setParams(MapBuilder.<String, String>create().
                    put(DelFlag.DEL_FLAG, DelFlag.EXIST.getCodeStr()).build());
        } else {
            params.put(DelFlag.DEL_FLAG, DelFlag.EXIST.getCodeStr());
        }
    }

    /**
    * 添加默认排序
     */
    public void addDefaultOrder(String orderField, Boolean asc){
        if (CollUtil.isEmpty(filterNullOrders())){
            this.orders = Lists.newArrayList(new Order(orderField, asc));
        }
    }

    /**
     * 判断某排序是否存在
     *
     *  用于统计数字排序
     *  此方法必须放在{#convert}方法之前
     * @return 返回null表示没有排序，否则表示排序是升序还是降序
     *
     */
    public Boolean judgeOrderByField(String fieldName, boolean isRemove) {
        Boolean numOrder = null;
        Order orderByFiledName = getOrderByFiledName(fieldName);
        if (orderByFiledName != null){
            numOrder = orderByFiledName.getAsc();
            if (isRemove){
                this.orders.remove(orderByFiledName);
            }
        }
        return numOrder;
    }

    /**
     * 按照数字排序的比较器
     * 配合 #judgeOrderByField使用
     * 将结果collent.stream().sorted(getNumberComparator( , )).collent返回
     */
    public <T> Comparator<T> getNumberComparator(final Boolean numOrder, Function<T, Integer> function){
        return (a,b)-> numOrder?function.apply(a) - function.apply(b): function.apply(b) -function.apply(a);
    }
}
